package main

import (
	"github.com/gogf/gf/text/gstr"
	"github.com/tidwall/gjson"
)

type Parser struct {
	models           map[string]gjson.Result
	handleAssocModel func(table *Table)
	handleOptions    func(_type string, title string, options gjson.Result)
}

// 解析模型时用到的Flag
const (
	SurfaceFlag = 1 << iota // 表示只解析模型本身和主键
	InlineFlag              // 表示当前模型如果是内嵌模型，可以一起提交
	ExtendFlag              // 表示当前模型是annex之类的字段的关联模型，需要读取具体字段
)

/*
parseModel 将schema中指定的模型解析为*Table
name 模型名称
json 模型的schema
flag 生成标记
*/
func (p Parser) parseModel(name string, json gjson.Result, flag int) (table *Table) {
	title := json.Get("title").String()
	group := json.Get("group").String()
	source := struct {
		Table    string
		Features gjson.Result
	}{}
	if sourceJson := json.Get("source"); sourceJson.IsObject() {
		source.Table = sourceJson.Get("table").String()
		source.Features = sourceJson.Get("features")
	} else {
		source.Table = sourceJson.String()
	}

	table = &Table{
		TableName:      source.Table,
		TableComment:   title,
		ClassName:      gstr.CaseCamel(name),
		TplCategory:    "crud",
		PackageName:    "gfast/app/" + group,
		ModuleName:     group,
		BusinessName:   name,
		FunctionName:   title,
		FunctionAuthor: "guo",
	}
	pk := &Column{
		ColumnName:    "id",
		ColumnComment: "id",
		ColumnType:    "int(11)",
		GoType:        "int",
		GoField:       "Id",
		HtmlField:     "id",
		IsPk:          "1",
		IsIncrement:   "1",
		IsRequired:    "",
		IsInsert:      "",
		IsEdit:        "",
		IsList:        "",
		IsQuery:       "",
		QueryType:     "EQ",
		HtmlType:      "input",
		DictType:      "",
		Sort:          1,
	}
	for _, feature := range source.Features.Array() {
		if feature.Get("type").String() == "default" {
			key := feature.Get("key").String()
			pk.ColumnName = key
			pk.GoField = gstr.CaseCamel(key)
			pk.HtmlField = gstr.CaseCamelLower(key)
			break
		}
	}
	table.PkColumn = pk
	table.Columns = []*Column{pk}
	if flag&SurfaceFlag != 0 {
		return
	}

	json.Get("attributes").ForEach(func(key, attr gjson.Result) bool {
		column := p.parseAttr(table, key.String(), attr, flag)
		if column.HtmlType == "annex" {
			table.Tabs = append(table.Tabs, column)
		} else {
			table.Columns = append(table.Columns, column)
		}
		return true
	})
	json.Get("actions").ForEach(func(key, json gjson.Result) bool {
		action := p.parseAction(table, key.String(), json, flag)
		table.Actions = append(table.Actions, action)
		return true
	})
	return
}

func (p Parser) parseAttr(table *Table, name string, json gjson.Result, flag int) *Column {
	title := json.Get("title").String()
	_type := json.Get("type").String()
	required := "0"
	if json.Get("required").Bool() {
		required = "1"
	}
	show := json.Get("show").String()

	column := &Column{
		ColumnName:    name,
		ColumnComment: title,
		GoField:       gstr.CaseCamel(name),
		HtmlField:     gstr.CaseCamelLower(name),
		IsPk:          "0",
		IsIncrement:   "0",
		IsRequired:    required,
		IsInsert:      "1",
		IsEdit:        "1",
		IsList:        "1",
		DictType:      "",
		Sort:          1,
		IsShow:        show,
	}

	// 根据属性类型设置在不同环境中的类型
	switch _type {
	case "string":
		length := "40"
		if json.Get("maxLength").Exists() {
			length = json.Get("maxLength").String()
		}
		column.ColumnType = "varchar(" + length + ")"
		column.GoType = "string"
		column.HtmlType = "input"
	case "text":
		column.ColumnType = "text"
		column.GoType = "string"
		column.HtmlType = "textarea"
	case "richtext":
		column.ColumnType = "text"
		column.GoType = "string"
		column.HtmlType = "richtext"
		column.IsList = "0"
	case "integer":
		column.ColumnType = "int(11)"
		column.GoType = "int"
		column.HtmlType = "number"
	case "datetime":
		column.ColumnType = "datetime"
		column.GoType = "Time"
		column.HtmlType = "datetime"
	case "date":
		column.ColumnType = "datetime"
		column.GoType = "Time"
		column.HtmlType = "date"
	case "time":
		column.ColumnType = "datetime"
		column.GoType = "Time"
		column.HtmlType = "time"
	case "duration":
		column.ColumnType = "int(11)"
		column.GoType = "int"
		column.HtmlType = "duration"
	case "enable":
		column.ColumnType = "int(11)"
		column.GoType = "int"
		column.HtmlType = "enable"
	case "select":
		column.ColumnType = "int(11)"
		column.GoType = "int"
		column.HtmlType = "select"
	case "radio":
		column.ColumnType = "int(11)"
		column.GoType = "int"
		column.HtmlType = "radio"
	case "checkbox":
		column.ColumnType = "varchar(40)"
		column.GoType = "string"
		column.HtmlType = "checkbox"
	case "bitcode":
		column.ColumnType = "int(11)"
		column.GoType = "int"
		column.HtmlType = "bitcode"
	case "image":
		column.ColumnType = "varchar(100)"
		column.GoType = "string"
		column.HtmlType = "imagefile"
	case "refer":
		column.ColumnType = "int(11)"
		column.GoType = "int"
		column.HtmlType = "select"
	case "tree":
		column.ColumnType = "int(11)"
		column.GoType = "int"
		column.HtmlType = "treeselect"
	case "annex":
		column.HtmlType = "annex"
	default:
		column.ColumnType = "varchar(40)"
		column.GoType = "string"
		column.HtmlType = "input"
	}

	// 如果属性有关联表，则设置关联表
	if assoc := json.Get("assoc"); assoc.Exists() {
		_flag := flag
		switch _type {
		case "annex":
			_flag |= ExtendFlag
		default:
			_flag |= SurfaceFlag
		}
		data, link := p.parseAttrAssoc(table, column, assoc, _flag)
		column.LinkLabelId = data.KeyField
		column.LinkLabelName = data.LabelField
		column.LinkTablePackage = link.PackageName
		column.LinkTableModule = link.ModuleName
		column.LinkTableName = link.TableName
		column.LinkTableClass = link.ClassName
	}

	// 如果属性有选项，则写入到字典中
	if options := json.Get("options"); options.Exists() {
		key := table.TableName + "_" + name
		title := table.FunctionName + json.Get("title").String()
		p.handleOptions(key, title, options)
		column.DictType = key
	}

	// 如果属性可以在查询时使用，则设置查询
	if query := json.Get("query"); query.IsBool() {
		column.IsQuery = "1"
		column.QueryType = "EQ"
	} else if !query.IsObject() {
		switch query.String() {
		case "like":
			column.IsQuery = "1"
			column.QueryType = "LIKE"
		}
	}
	return column
}

func (p Parser) parseAttrAssoc(table *Table, column *Column, json gjson.Result, flag int) (assoc *Assoc, link *Table) {
	if json.IsObject() {
		if mJson := json.Get("model"); mJson.IsObject() {
			link = p.parseModel(table.BusinessName+"_"+column.ColumnName, mJson, flag)
		} else {
			mName := mJson.String()
			mJson2 := p.models[mName]
			flag &= ^InlineFlag
			link = p.parseModel(mName, mJson2, flag)
		}
		assoc = &Assoc{
			KeyField:   json.Get("key").String(),
			LabelField: json.Get("label").String(),
			Fields:     nil,
			Filter:     "",
			Order:      "",
		}
	} else {
		mJson := p.models[json.String()]
		flag &= ^InlineFlag
		link = p.parseModel(json.String(), mJson, flag)
		json = mJson.Get("assoc")
		assoc = &Assoc{
			KeyField:   json.Get("key").String(),
			LabelField: json.Get("label").String(),
			Fields:     nil,
			Filter:     "",
			Order:      "",
		}
	}

	if column.HtmlType == "annex" {
		has := false
		for _, col := range link.Columns {
			if col.ColumnName == assoc.KeyField {
				has = true
				col.HtmlType = "underlying"
			}
		}
		if !has {
			col := table.PkColumn.Copy()
			col.IsPk = "0"
			col.IsIncrement = "0"
			col.HtmlType = "underlying"
			col.IsInsert = "1"
			col.IsEdit = "1"
			key := assoc.KeyField
			col.ColumnName = key
			col.GoField = gstr.CaseCamel(key)
			col.HtmlField = gstr.CaseCamelLower(key)
			link.Columns = append(link.Columns, col)
		}
	}

	if flag&InlineFlag != 0 {
		link.IsEmbed = true
		p.handleAssocModel(link)
	}
	return
}

func (p Parser) parseAction(table *Table, name string, json gjson.Result, flag int) (action *Action) {
	link := &Table{}
	if linkJson := json.Get("model"); linkJson.Exists() {
		acName := json.Get("action").String()
		mJson := p.models[linkJson.String()]
		acJson := mJson.Get("actions").Get(acName)
		action = &Action{}
		action.Parse(acJson)
		action.Parse(json)
		flag = flag &^ InlineFlag
		link = p.parseModel(linkJson.String(), mJson, flag)
	} else {
		action = &Action{}
		action.Parse(json)
		link = table.Copy()
	}
	action.Table = link

	action.Name = name

	switch action.Type {
	case "view", "patch", "refer":
		action.RowSetting = "single"
	default:
		action.RowSetting = ""
	}

	exclude := map[string]struct{}{}
	for _, attr := range action.Exclude {
		exclude[attr] = struct{}{}
	}
	columns := make([]*Column, 0)
	for _, column := range link.Columns {
		if _, has := exclude[column.ColumnName]; !has {
			columns = append(columns, column)
		}
	}
	link.Columns = columns

	switch action.Type {
	case "view", "patch":
		for _, column := range link.Columns {
			column.Immutable = "1"
		}
	}

	for _, column := range link.Columns {
		if v, has := action.Preset[column.ColumnName]; has {
			column.Default = v
			column.Immutable = "1"
		}
	}

	for _, column := range link.Columns {
		if v, has := action.Deliver[column.ColumnName]; has {
			column.Deliver = v
			column.Immutable = "1"
		}
	}

	mutable := map[string]struct{}{}
	for _, field := range action.Mutable {
		mutable[field] = struct{}{}
	}
	for _, column := range link.Columns {
		if _, has := mutable[column.ColumnName]; has {
			column.Immutable = "0"
		}
	}
	return
}

//func (p Parser) ParseModel(name string, json gjson.Result, isRef bool, deep int) *Table {
//	module := func() string {
//		if group := json.Get("group").String(); group != "" {
//			return group
//		}
//		return "demo"
//	}()
//	source := json.Get("source")
//	table := &Table{
//		TableName: func() string {
//			if !source.IsObject() {
//				return source.String()
//			}
//			return source.Get("table").String()
//		}(),
//		TableComment:   json.Get("title").String(),
//		ClassName:      gstr.CaseCamel(name),
//		TplCategory:    "crud",
//		PackageName:    "gfast/app/" + module,
//		ModuleName:     module,
//		BusinessName:   name,
//		FunctionName:   json.Get("title").String(),
//		FunctionAuthor: "guo",
//		Options:        "",
//		TreeCode:       "",
//		TreeParentCode: "",
//		TreeName:       "",
//	}
//	pk := (*Column)(nil)
//	if source.IsObject() {
//		for _, feature := range source.Get("features").Array() {
//			if feature.Get("type").String() == "default" {
//				key := feature.Get("key").String()
//				pk = &Column{
//					ColumnName:    key,
//					ColumnComment: "id",
//					ColumnType:    "int(11)",
//					GoType:        "int",
//					GoField:       gstr.CaseCamel(key),
//					HtmlField:     gstr.CaseCamelLower(key),
//					IsPk:          "1",
//					IsIncrement:   "1",
//					IsRequired:    "",
//					IsInsert:      "",
//					IsEdit:        "",
//					IsList:        "",
//					IsQuery:       "",
//					QueryType:     "EQ",
//					HtmlType:      "input",
//					DictType:      "",
//					Sort:          1,
//				}
//				break
//			}
//		}
//	}
//	if pk == nil {
//		pk = &Column{
//			ColumnName:    "id",
//			ColumnComment: "",
//			ColumnType:    "int(11)",
//			GoType:        "int",
//			GoField:       "Id",
//			HtmlField:     "id",
//			IsPk:          "1",
//			IsIncrement:   "1",
//			IsRequired:    "",
//			IsInsert:      "",
//			IsEdit:        "",
//			IsList:        "",
//			IsQuery:       "",
//			QueryType:     "EQ",
//			HtmlType:      "input",
//			DictType:      "",
//			Sort:          1,
//		}
//	}
//	table.PkColumn = pk
//	table.Columns = []*Column{pk}
//	json.Get("attributes").ForEach(func(key, attr gjson.Result) bool {
//		column := p.ParseAttr(table, key.String(), attr, isRef, deep)
//		if column.HtmlType == "annex" {
//			table.Tabs = append(table.Tabs, column)
//		} else {
//			table.Columns = append(table.Columns, column)
//		}
//		return true
//	})
//	json.Get("actions").ForEach(func(key, json gjson.Result) bool {
//		action := p.ParseAction(table, key.String(), json, deep)
//		table.Actions = append(table.Actions, action)
//		return true
//	})
//	return table
//}
//
//func (p Parser) ParseActionModel(name string, json gjson.Result, isRef bool, deep int) *Table {
//	module := func() string {
//		if group := json.Get("group").String(); group != "" {
//			return group
//		}
//		return "demo"
//	}()
//	source := json.Get("source")
//	table := &Table{
//		TableName: func() string {
//			if !source.IsObject() {
//				return source.String()
//			}
//			return source.Get("table").String()
//		}(),
//		TableComment:   json.Get("title").String(),
//		ClassName:      gstr.CaseCamel(name),
//		TplCategory:    "crud",
//		PackageName:    "gfast/app/" + module,
//		ModuleName:     module,
//		BusinessName:   name,
//		FunctionName:   json.Get("title").String(),
//		FunctionAuthor: "guo",
//		Options:        "",
//		TreeCode:       "",
//		TreeParentCode: "",
//		TreeName:       "",
//	}
//	pk := (*Column)(nil)
//	if source.IsObject() {
//		for _, feature := range source.Get("features").Array() {
//			if feature.Get("type").String() == "default" {
//				key := feature.Get("key").String()
//				pk = &Column{
//					ColumnName:    key,
//					ColumnComment: "id",
//					ColumnType:    "int(11)",
//					GoType:        "int",
//					GoField:       gstr.CaseCamel(key),
//					HtmlField:     gstr.CaseCamelLower(key),
//					IsPk:          "1",
//					IsIncrement:   "1",
//					IsRequired:    "",
//					IsInsert:      "",
//					IsEdit:        "",
//					IsList:        "",
//					IsQuery:       "",
//					QueryType:     "EQ",
//					HtmlType:      "input",
//					DictType:      "",
//					Sort:          1,
//				}
//				break
//			}
//		}
//	}
//	if pk == nil {
//		pk = &Column{
//			ColumnName:    "id",
//			ColumnComment: "",
//			ColumnType:    "int(11)",
//			GoType:        "int",
//			GoField:       "Id",
//			HtmlField:     "id",
//			IsPk:          "1",
//			IsIncrement:   "1",
//			IsRequired:    "",
//			IsInsert:      "",
//			IsEdit:        "",
//			IsList:        "",
//			IsQuery:       "",
//			QueryType:     "EQ",
//			HtmlType:      "input",
//			DictType:      "",
//			Sort:          1,
//		}
//	}
//	table.PkColumn = pk
//	table.Columns = []*Column{pk}
//	json.Get("attributes").ForEach(func(key, attr gjson.Result) bool {
//		column := p.ParseAttr(table, key.String(), attr, isRef, deep)
//		if column.HtmlType == "annex" {
//			table.Tabs = append(table.Tabs, column)
//		} else {
//			table.Columns = append(table.Columns, column)
//		}
//		return true
//	})
//	return table
//}
//
//func (p Parser) ParseAttrAssocModel(name string, json gjson.Result, isRef bool, deep int) *Table {
//	if deep < 0 {
//		return &Table{}
//	}
//	module := func() string {
//		if group := json.Get("group").String(); group != "" {
//			return group
//		}
//		return "demo"
//	}()
//	source := json.Get("source")
//	table := &Table{
//		TableName: func() string {
//			if !source.IsObject() {
//				return source.String()
//			}
//			return source.Get("table").String()
//		}(),
//		TableComment:   json.Get("title").String(),
//		ClassName:      gstr.CaseCamel(name),
//		TplCategory:    "crud",
//		PackageName:    "gfast/app/" + module,
//		ModuleName:     module,
//		BusinessName:   name,
//		FunctionName:   json.Get("title").String(),
//		FunctionAuthor: "guo",
//		Options:        "",
//		TreeCode:       "",
//		TreeParentCode: "",
//		TreeName:       "",
//	}
//	pk := (*Column)(nil)
//	if source.IsObject() {
//		for _, feature := range source.Get("features").Array() {
//			if feature.Get("type").String() == "default" {
//				key := feature.Get("key").String()
//				pk = &Column{
//					ColumnName:    key,
//					ColumnComment: "id",
//					ColumnType:    "int(11)",
//					GoType:        "int",
//					GoField:       gstr.CaseCamel(key),
//					HtmlField:     gstr.CaseCamelLower(key),
//					IsPk:          "1",
//					IsIncrement:   "1",
//					IsRequired:    "",
//					IsInsert:      "",
//					IsEdit:        "",
//					IsList:        "",
//					IsQuery:       "",
//					QueryType:     "EQ",
//					HtmlType:      "input",
//					DictType:      "",
//					Sort:          1,
//				}
//				break
//			}
//		}
//	}
//	if pk == nil {
//		pk = &Column{
//			ColumnName:    "id",
//			ColumnComment: "",
//			ColumnType:    "int(11)",
//			GoType:        "int",
//			GoField:       "Id",
//			HtmlField:     "id",
//			IsPk:          "1",
//			IsIncrement:   "1",
//			IsRequired:    "",
//			IsInsert:      "",
//			IsEdit:        "",
//			IsList:        "",
//			IsQuery:       "",
//			QueryType:     "EQ",
//			HtmlType:      "input",
//			DictType:      "",
//			Sort:          1,
//		}
//	}
//	table.PkColumn = pk
//	table.Columns = []*Column{pk}
//	json.Get("attributes").ForEach(func(key, attr gjson.Result) bool {
//		column := p.ParseAttr(table, key.String(), attr, isRef, deep)
//		if column.HtmlType == "annex" {
//			table.Tabs = append(table.Tabs, column)
//		} else {
//			table.Columns = append(table.Columns, column)
//		}
//		return true
//	})
//	return table
//}
//
//func (p Parser) ParseAttr(table *Table, name string, json gjson.Result, isRef bool, deep int) *Column {
//	column := &Column{
//		ColumnName:    name,
//		ColumnComment: json.Get("title").String(),
//		GoField:       gstr.CaseCamel(name),
//		HtmlField:     gstr.CaseCamelLower(name),
//		IsPk:          "0",
//		IsIncrement:   "0",
//		IsRequired: func() string {
//			if json.Get("required").Bool() {
//				return "1"
//			} else {
//				return "0"
//			}
//		}(),
//		IsInsert: "1",
//		IsEdit:   "1",
//		IsList:   "1",
//		DictType: "",
//		Sort:     1,
//	}
//
//	// 根据属性类型设置在不同环境中的类型
//	// TODO 需要增加在schema中指定特定环境中的类型的功能
//	switch json.Get("type").String() {
//	case "string":
//		length := "40"
//		if json.Get("maxLength").Exists() {
//			length = json.Get("maxLength").String()
//		}
//		column.ColumnType = "varchar(" + length + ")"
//		column.GoType = "string"
//		column.HtmlType = "input"
//	case "text":
//		column.ColumnType = "text"
//		column.GoType = "string"
//		column.HtmlType = "textarea"
//	case "richtext":
//		column.ColumnType = "text"
//		column.GoType = "string"
//		column.HtmlType = "richtext"
//	case "integer":
//		column.ColumnType = "int(11)"
//		column.GoType = "int"
//		column.HtmlType = "number"
//	case "datetime":
//		column.ColumnType = "datetime"
//		column.GoType = "Time"
//		column.HtmlType = "datetime"
//	case "select":
//		column.ColumnType = "int(11)"
//		column.GoType = "int"
//		column.HtmlType = "select"
//	case "radio":
//		column.ColumnType = "int(11)"
//		column.GoType = "int"
//		column.HtmlType = "radio"
//	case "bitcode":
//		column.ColumnType = "int(11)"
//		column.GoType = "int"
//		column.HtmlType = "bitcode"
//	case "image":
//		column.ColumnType = "varchar(100)"
//		column.GoType = "string"
//		column.HtmlType = "imagefile"
//	case "refer":
//		column.ColumnType = "int(11)"
//		column.GoType = "int"
//		column.HtmlType = "select"
//	case "tree":
//		column.ColumnType = "int(11)"
//		column.GoType = "int"
//		column.HtmlType = "treeselect"
//	case "annex":
//		column.HtmlType = "annex"
//	default:
//		column.ColumnType = "varchar(40)"
//		column.GoType = "string"
//		column.HtmlType = "input"
//	}
//
//	// 如果属性有关联表，则设置关联表
//	if assoc := json.Get("assoc"); assoc.Exists() {
//		data, link := p.ParseAttrAssoc(table, column, assoc, isRef, deep)
//		column.LinkLabelId = data.KeyField
//		column.LinkLabelName = data.LabelField
//		column.LinkTablePackage = link.PackageName
//		column.LinkTableModule = link.ModuleName
//		column.LinkTableName = link.TableName
//		column.LinkTableClass = link.ClassName
//		if link.IsEmbed {
//			p.handleAssocModel(link)
//		}
//	}
//
//	// 如果属性有选项，则写入到字典中
//	if options := json.Get("options"); options.Exists() {
//		key := table.TableName + "_" + name
//		title := table.FunctionName + json.Get("title").String()
//		p.handleOptions(key, title, options)
//		column.DictType = key
//	}
//
//	// 如果属性可以在查询时使用，则设置查询
//	if query := json.Get("query"); query.IsBool() {
//		column.IsQuery = "1"
//		column.QueryType = "EQ"
//	} else if !query.IsObject() {
//		switch query.String() {
//		case "like":
//			column.IsQuery = "1"
//			column.QueryType = "LIKE"
//		}
//	}
//	return column
//}
//
//func (p Parser) ParseAttrAssoc(table *Table, column *Column, json gjson.Result, isRef bool, deep int) (assoc *Assoc, link *Table) {
//	if json.IsObject() {
//		if mJson := json.Get("model"); mJson.IsObject() {
//			link = p.ParseAttrAssocModel(table.BusinessName+"_"+column.ColumnName, mJson, isRef, deep-1)
//			if !isRef {
//				link.IsEmbed = true
//			}
//		} else {
//			mName := mJson.String()
//			mJson2 := p.models[mName]
//			link = p.ParseAttrAssocModel(mName, mJson2, true, deep-1)
//		}
//		assoc = &Assoc{
//			KeyField:   json.Get("key").String(),
//			LabelField: json.Get("label").String(),
//			Fields:     nil,
//			Filter:     "",
//			Order:      "",
//		}
//	} else {
//		mJson := p.models[json.String()]
//		link = p.ParseAttrAssocModel(json.String(), mJson, true, deep-1)
//		if !isRef {
//			link.IsEmbed = false
//		}
//		json = mJson.Get("assoc")
//		assoc = &Assoc{
//			KeyField:   json.Get("key").String(),
//			LabelField: json.Get("label").String(),
//			Fields:     nil,
//			Filter:     "",
//			Order:      "",
//		}
//		isRef = true
//	}
//
//	if column.HtmlType == "annex" {
//		has := false
//		for _, col := range link.Columns {
//			if col.ColumnName == assoc.KeyField {
//				has = true
//				col.HtmlType = "underlying"
//			}
//		}
//		if !has {
//			col := table.PkColumn.Copy()
//			col.IsPk = "0"
//			col.IsIncrement = "0"
//			col.HtmlType = "underlying"
//			col.IsInsert = "1"
//			col.IsEdit = "1"
//			key := assoc.KeyField
//			col.ColumnName = key
//			col.GoField = gstr.CaseCamel(key)
//			col.HtmlField = gstr.CaseCamelLower(key)
//			link.Columns = append(link.Columns, col)
//		}
//	}
//	return
//}
//
//func (p Parser) ParseAction(table *Table, name string, json gjson.Result, deep int) (ac *Action) {
//	ta := &Table{}
//	if link := json.Get("model"); link.Exists() {
//		acName := json.Get("action").String()
//		mJson := p.models[link.String()]
//		action := mJson.Get("actions").Get(acName)
//		ac = &Action{}
//		ac.Parse(action)
//		ac.Parse(json)
//		ta = p.ParseActionModel(link.String(), mJson, true, deep-1)
//	} else {
//		ac = &Action{}
//		ac.Parse(json)
//		ta = table.Copy()
//	}
//	ac.Table = ta
//
//	ac.Name = name
//
//	switch ac.Type {
//	case "view", "patch", "refer":
//		ac.RowSetting = "single"
//	default:
//		ac.RowSetting = ""
//	}
//
//	exclude := map[string]struct{}{}
//	for _, attr := range ac.Exclude {
//		exclude[attr] = struct{}{}
//	}
//	columns := make([]*Column, 0)
//	for _, column := range ta.Columns {
//		if _, has := exclude[column.ColumnName]; !has {
//			columns = append(columns, column)
//		}
//	}
//	ta.Columns = columns
//
//	switch ac.Type {
//	case "view", "patch":
//		for _, column := range ta.Columns {
//			column.Immutable = "1"
//		}
//	}
//
//	for _, column := range ta.Columns {
//		if v, has := ac.Preset[column.ColumnName]; has {
//			column.Default = v
//			column.Immutable = "1"
//		}
//	}
//
//	for _, column := range ta.Columns {
//		if v, has := ac.Deliver[column.ColumnName]; has {
//			column.Deliver = v
//			column.Immutable = "1"
//		}
//	}
//
//	mutable := map[string]struct{}{}
//	for _, field := range ac.Mutable {
//		mutable[field] = struct{}{}
//	}
//	for _, column := range ta.Columns {
//		if _, has := mutable[column.ColumnName]; has {
//			column.Immutable = "0"
//		}
//	}
//	return
//}
